package cz.cvut.fit.mi.dpo.mvc.view.graphics;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.BorderFactory;

import cz.cvut.fit.mi.dpo.mvc.controller.Controller;
import cz.cvut.fit.mi.dpo.mvc.model.IReadable;
import cz.cvut.fit.mi.dpo.mvc.model.elements.AElement;
import cz.cvut.fit.mi.dpo.mvc.model.elements.Circle;
import cz.cvut.fit.mi.dpo.mvc.model.elements.Square;
import cz.cvut.fit.mi.dpo.mvc.view.AView;

/**
 * @author David Pertl
 * @author Martin Štekl <steklmar@fit.cvut.cz>
 */
public class GraphicsView extends AView {

	public GraphicsView(Controller controller, IReadable model) {
		super(controller, model);
		setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 2));
		setBackground(Color.white);

		MouseAdapter mouseAdapter = new MouseEventAdapter();
		addMouseListener(mouseAdapter);
		addMouseMotionListener(mouseAdapter);

		setPreferredSize(new Dimension(0, 0));
	}

	private class MouseEventAdapter extends MouseAdapter {
		private boolean isDragging = false;
		private AElement draggedObject = null;
		private int dragFromX;
		private int dragFromY;

		@Override
		public void mousePressed(MouseEvent event) {
			int x = event.getX();
			int y = event.getY();
			for (Square el : model.getSquares()) {
				if (isDragging) {
					break;
				}
				checkDragging(el, x, y);
			}
			for (Circle el : model.getCircles()) {
				if (isDragging) {
					break;
				}
				checkDragging(el, x, y);
			}
		}

		private void checkDragging(Circle el, int x, int y) {
			if (isPressed(el, x, y)) {
				setDragging(el, x, y);
			} else {
				unsetDragging();
			}
		}

		private void checkDragging(Square el, int x, int y) {
			if (isPressed(el, x, y)) {
				setDragging(el, x, y);
			} else {
				unsetDragging();
			}
		}

		private void unsetDragging() {
			isDragging = false;
			draggedObject = null;
		}

		private void setDragging(AElement el, int x, int y) {
			isDragging = true;
			draggedObject = el;
			dragFromX = x - el.getX();
			dragFromY = y - el.getY();
		}

		private boolean isPressed(Circle circle, int x, int y) {
			int centerX = circle.getX() + circle.getRadius();
			int centerY = circle.getY() + circle.getRadius();
			int xFromCenter = x - centerX;
			int yFromCenter = y - centerY;
			int lengthSquared = xFromCenter*xFromCenter + yFromCenter*yFromCenter;
			return (
				x >= circle.getX() && x <= (circle.getX() + circle.getDiameter())
				&& y >= circle.getY() && y <= (circle.getY() + circle.getDiameter())
				&& lengthSquared <= (circle.getRadius()*circle.getRadius())
			);
		}

		private boolean isPressed(Square square, int x, int y) {
			return (
				x >= square.getX() && x <= (square.getX() + square.getEdgeLength())
				&& y >= square.getY() && y <= (square.getY() + square.getEdgeLength())
			);
		}

		@Override
		public void mouseReleased(MouseEvent event) {
			unsetDragging();
		}

		@Override
		public void mouseExited(MouseEvent event) {
			unsetDragging();
		}

		@Override
		public void mouseDragged(MouseEvent event) {
			if (isDragging) {
				int newX = Math.max(event.getX() - dragFromX, 0);
				int newY = Math.max(event.getY() - dragFromY, 0);
				drag(draggedObject, newX, newY);
			}
		}

		private void drag(AElement el, int newX, int newY) {
			if (el instanceof Circle) {
				drag((Circle) el, newX, newY);
			} else if (el instanceof Square) {
				drag((Square) el, newX, newY);
			}
		}

		private void drag(Circle circle, int newX, int newY) {
			newX = Math.min(newX, getWidth() - circle.getDiameter());
			newY = Math.min(newY, getHeight() - circle.getDiameter());
			controller.editCircle(circle.getId(), newX, newY, circle.getRadius());
		}

		private void drag(Square sqaure, int newX, int newY) {
			newX = Math.min(newX, getWidth() - sqaure.getEdgeLength());
			newY = Math.min(newY, getHeight() - sqaure.getEdgeLength());
			controller.editSquare(sqaure.getId(), newX, newY, sqaure.getEdgeLength());
		}

		@Override
		public void mouseClicked(MouseEvent event) {
			if (event.getButton() == MouseEvent.BUTTON3) {
				controller.addSquare(event.getX(), event.getY());
			} else if (event.getButton() == MouseEvent.BUTTON1) {
				controller.addCircle(event.getX(), event.getY());
			}
		}
	}

	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2d = (Graphics2D) g;
		g2d.setPaintMode();
		Color[] colors = new Color[]{Color.BLACK, Color.RED, Color.GREEN, Color.BLUE};
		Random random = new Random();

		random.setSeed(42);
		for (Square s : model.getSquares()) {
			g2d.setStroke(new BasicStroke(1));
			g2d.setColor(colors[Math.abs(random.nextInt()) % colors.length]);
			g2d.fillRect(s.getX(), s.getY(), s.getEdgeLength(), s.getEdgeLength());
		}

		random.setSeed(43);
		for (Circle c : model.getCircles()) {
			g2d.setStroke(new BasicStroke(1));
			g2d.setColor(colors[Math.abs(random.nextInt()) % colors.length]);
			g2d.fillOval(c.getX(), c.getY(), c.getDiameter(), c.getDiameter());
		}
	}

}
